﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JihankiLib
{
    /// <summary>
    /// お金種類に紐付ける金額
    /// </summary>
    public class MoneyUnitValueAttribute : Attribute
    {
        public int Unit;
        public MoneyUnitValueAttribute(int unit)
        {
            this.Unit = unit;
        }

        public static int GetUnitValue(MoneyType mt)
        {
            var name = Enum.GetName(typeof(MoneyType), mt);
            var members = mt.GetType().GetMember(name);
            var attr = Attribute.GetCustomAttribute(members[0], typeof(MoneyUnitValueAttribute), false) as MoneyUnitValueAttribute;
            return attr.Unit;
        }
    }

    /// <summary>
    /// お金種類
    /// </summary>
    public enum MoneyType
    {
        [MoneyUnitValue(10)]
        COIN_10,
        [MoneyUnitValue(50)]
        COIN_50,
        [MoneyUnitValue(100)]
        COIN_100,
        [MoneyUnitValue(500)]
        COIN_500,
        [MoneyUnitValue(1000)]
        BILL_1000,
        [MoneyUnitValue(5000)]
        BILL_5000,
        [MoneyUnitValue(10000)]
        BILL_10000
    }

    /// <summary>
    /// お金
    /// </summary>
    public class Money
    {
        /// <summary>
        /// 単位
        /// </summary>
        public MoneyType Unit { get; private set; }
        /// <summary>
        /// 個数
        /// </summary>
        public int Amount { get; private set; }
        /// <summary>
        /// 金額
        /// </summary>
        public int Value { get { return MoneyUnitValueAttribute.GetUnitValue(Unit) * Amount; } }

        public Money(MoneyType unit, int amount)
        {
            this.Unit = unit;
            this.Amount = amount;
        }
    }

    /// <summary>
    /// お金セット
    /// </summary>
    public class MoneySet
    {
        private Dictionary<MoneyType, Money> onies = new Dictionary<MoneyType, Money>();

        public Dictionary<MoneyType, Money> Monies
        {
            get { return this.onies; }
        }

        public void Add(Money money)
        {
            if (onies.ContainsKey(money.Unit))
            {
                var now = onies[money.Unit];
                onies[money.Unit] = new Money(money.Unit, now.Amount + money.Amount);
            }
            else
            {
                this.onies.Add(money.Unit, money);
            }
        }

        public void Clear()
        {
            onies.Clear();
        }

        public int Value
        {
            get
            {
                int sum = 0;
                foreach (var key in this.onies.Keys)
                    sum += this.onies[key].Value;
                return sum;
            }
        }

        public int TypeTotal(MoneyType mt)
        {
            if(onies.ContainsKey(mt))
                return onies[mt].Value;
            return -1;
        }
    }

    public class AddedMoneyEventArgs : EventArgs
    {
        public MoneySet Added { get; set; }
        public MoneySet Total { get; set; }
    }

    public class RejectedAllMoneyEventArgs : EventArgs
    {
        public MoneySet RejectedValue { get; set; }
    }

    public class MoneyStateChangedEventArgs : EventArgs
    {
        public MoneyType MoneyType { get; set; }
    }

    /// <summary>
    /// お金ボックスの制御機器
    /// </summary>
    public class MoneyBoxMachine : IObserver<JihankiMessage>
    {
        /// <summary>
        /// お金が投入された
        /// </summary>
        public event EventHandler<AddedMoneyEventArgs> AddedMoney;
        /// <summary>
        /// 入金分が排出された
        /// </summary>
        public event EventHandler<RejectedAllMoneyEventArgs> RejectedAllMoney;
        /// <summary>
        /// 格納可能上限を超えた
        /// </summary>
        public event EventHandler<MoneyStateChangedEventArgs> UpperLimitOver;
        /// <summary>
        /// ストック警告下限を超えた
        /// </summary>
        public event EventHandler<MoneyStateChangedEventArgs> LowerLimitOver;
        /// <summary>
        /// お金ストックが空になった
        /// </summary>
        public event EventHandler<MoneyStateChangedEventArgs> EmptyMoney;

        protected void OnAddedMoney(MoneySet added, MoneySet currentTotal)
        {
            if (AddedMoney != null)
                AddedMoney(this, new AddedMoneyEventArgs() { Added = added, Total = currentTotal });
        }

        protected void OnRejectedAllMoney()
        {
            if (RejectedAllMoney != null)
                RejectedAllMoney(this, new RejectedAllMoneyEventArgs() { RejectedValue = total });
            total.Clear();
        }

        protected MoneySet total = new MoneySet();
        protected IDisposable jihanki = null;

        public void Initialize(IJihanki jihanki)
        {
            this.jihanki = jihanki.Subscribe(this);
        }

        public virtual void AddMoney(int money)
        {
            var current = MoneyParser.Current.Parse(money);
            total = MoneyParser.Current.Parse(current.Value + total.Value);
            OnAddedMoney(current, total);
        }

        public virtual void RejectAllMoney()
        {
            OnRejectedAllMoney();
        }

        public virtual void OnCompleted()
        {
            jihanki.Dispose();
        }

        public virtual void OnError(Exception error)
        {

        }

        public virtual void OnNext(JihankiMessage value)
        {
            //Todo: 各MessageProcesserクラスを作成し、そこにメッセージごとの処理を移動する。

            if (value.GetType() == typeof(ItemPurchasedMessage))
            {
                var message = value as ItemPurchasedMessage;
                total = MoneyParser.Current.Parse(total.Value - message.Money.Value);
                RejectAllMoney();
            }
        }
    }

}
